﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.ComponentModel;
using System.Windows.Forms;
using System.Xml;

using SourceGrid;
using iPdf.Properties;

namespace iPdf
{
    partial class TataGrid
    {
        #region Columns

        #region ColumnHeads
        public class ColumnHead : ColumnHeader
        {
            #region Fields
            public string Description;
            public bool Sortable;

            public delegate void ColumnCreator(string item);
            public ColumnCreator Creator;
            public delegate bool ColumnUpdator(int rowId);
            public ColumnUpdator Updator;

            #endregion

            #region Properties
            public new int Index
            {
                get
                {
                    return Array.IndexOf(columnHeads, this);
                }
            }
            #endregion

            #region Ctor
            public ColumnHead(string name, int width, string description, bool sortable)
            {
                Name = name;
                Width = width;
                Description = description;
                Sortable = sortable;
            }

            public ColumnHead(string name, int width, string description)
                : this(name, width, description, true)
            {
            }

            public ColumnHead(string name, int width)
                : this(name, width, string.Empty)
            {
            }

            public ColumnHead(string name)
                : this(name, 0)
            {
            }

            #endregion

            #region Methods
            public bool Update(int rowId)
            {
                return Updator == null ? true : Updator(rowId);
            }

            public void Create(string item)
            {
                if (Creator != null)
                {
                    Creator(item);
                }
            }

            #endregion
        }

        public class CheckBoxColumnHead : ColumnHead
        {
            private CheckState checkState = CheckState.Unchecked;

            public CheckState CheckState
            {
                get
                {
                    return checkState;
                }
                set
                {
                    if (checkState == value)
                    {
                        return;
                    }

                    checkState = value;

                    bool? isChecked = Helper.CheckStateToNullableBool(checkState);
                    Global.Grid.ChangeCheckBox(this, isChecked);
                }
            }

            public CheckBoxColumnHead(string name)
                :base(name)
            {
            }

            public CheckBoxColumnHead(string name, int width)
                : base(name, width)
            {
            }

            public CheckBoxColumnHead(string name, CheckState state)
                : base(name)
            {
                this.checkState = state;
            }

            public CheckBoxColumnHead(string name, int width, CheckState state)
                : base(name, width)
            {
                this.checkState = state;
            }

            public CheckBoxColumnHead(string name, int width, string description)
                : base(name, width, description)
            {
            }
        }

        public static ColumnHead ID = new ColumnHead("ID", 35, string.Empty, false);
        public static ColumnHead Source = new ColumnHead("Source", 0, "Path of the file");
        public static ColumnHead Destination = new ColumnHead("Destination", 0, "Path of the new file");
        public static ColumnHead From = new ColumnHead("From", 50, "Start page no");
        public static ColumnHead To = new ColumnHead("To", 30, "End page no");
        public static CheckBoxColumnHead Bookmark = new CheckBoxColumnHead("Bookmark", 75, "Keep bookmarks");
        public static CheckBoxColumnHead Hyperlink = new CheckBoxColumnHead("Hyperlink", 70, "Keep bookmarks");
        public static CheckBoxColumnHead Navigator = new CheckBoxColumnHead("Navigator", 75, "Add navigate table");
        public static ColumnHead Status = new ColumnHead("Status", 70, "Working status of the item");
        public static ColumnHead Password = new ColumnHead("Password", 75, "Owner password of the file");

        protected static ColumnHead[] columnHeads = new ColumnHead[] { ID, Source, Password, Destination, From, To,
                        Bookmark, Hyperlink, Navigator, Status } ;

        #endregion

        private struct TataGridItem
        {
            public string FileName;
            public int RowIndex;

            public TataGridItem(string fileName, int rowId)
            {
                FileName = fileName;
                RowIndex = rowId;
            }
        }

        #region Creators

        private void CreatorIDColumn(string item)
        {
            this[RowsCount - 1, ID.Index] = CreateRowHeader(RowsCount-1);
        }

        private void CreateSourceColumn(string item)
        {
            this[RowsCount - 1, Source.Index] = CreateFileNameCell(sourceCellTooltip, null, item);
        }

        private void CreateDestinationColumn(string item)
        {
            bool visible = IsColumnVisible(Destination);
            if (visible)
            {
                CreateDestinationCell(RowsCount - 1, GetDestination(item));
            }
        }

        private void CreateFromToColumns(string item)
        {
            bool visible = IsColumnVisible(From) && IsColumnVisible(To);

            if (visible)
            {
                CreateFromToCell(RowsCount - 1, item);
            }
        }


        /// <summary>
        /// Create check box by colmun
        /// </summary>
        /// <param name="head">The column head</param>
        /// <param name="isChecked">The value to be set</param>
        private void CreateCheckBoxByColumn(CheckBoxColumnHead head)
        {
            bool visible = IsColumnVisible(head);
            if (visible)
            {
                this[RowsCount - 1, head.Index] = CreateCheckBox(
                                                                        Helper.CheckStateToNullableBool(head.CheckState));
            }
        }

        private void CreateCheckBoxColumns(string item)
        {
            CreateCheckBoxByColumn(Bookmark);
            CreateCheckBoxByColumn(Hyperlink);
            CreateCheckBoxByColumn(Navigator);
        }

        private void CreateStatusColumn(string item)
        {
            this[RowsCount - 1, Status.Index] = CreateRegularCell(ItemStatus.Idle.ToString());
        }

        private void CreatePasswordColumn(string item)
        {
            bool visible = IsColumnVisible(Password);

            if (visible)
            {
                CreatePasswordCell(RowsCount - 1);
            }
        }
        #endregion

        #region Updators

        //Hide/remove/show/update cells
        public void UpdateCells()
        {
            if (Global.MainWindow.Busy)
            {
                EnableAutoSize(false);
                EnableControllerAndEditor(false);
                EnableSorting(false);
                return;
            }

            destinatinCellEditor.Extension = Global.CurrentPage.DestinationFormat.Extension;

            if (SelectionExists && Global.MainWindow.TabPageChanging)
            {
                Selection.ResetSelection(false);
            }

            EnableAutoSize(true);
            EnableControllerAndEditor(true);
            EnableSorting(true);
            
            int removedItemCnt = 0;
            int adjustedIndex;
            int rowsCnt = RowsCount;
            for (int i = 0; i < rowsCnt; ++i)
            {
                adjustedIndex = i - removedItemCnt;
                foreach (ColumnHead colHead in columnHeads)
                {
                    if (!colHead.Update(adjustedIndex))
                    {
                        ++removedItemCnt;
                        break;
                    }
                }
            }

            AutoSizeCells();

            //cause the height of the rows could be changed, 
            //we need to reset the height of the rows
            for (int i = 1; i < RowsCount; ++i)
            {
                Rows[i].Height = gridRowHeight;
            }

            PerformLayout();
             
            UpdatePdfLetsFlags();

            Helper.CollectGarbage();
        }

        private bool UpdateIDColumn(int rowId)
        {
            if (rowId == 0)
            {
                return true;
            }
            SourceGrid.Cells.Cell idCell = (SourceGrid.Cells.Cell)this[rowId, ID.Index];
            idCell.Value = rowId;

            return true;
        }

        private bool UpdateSourceColumn(int rowId)
        {
            if (rowId > 0 && Global.MainWindow.TabPageChanging && 
                Global.MainWindow.PreviousPage != null)
            {
                FileFormat format = Global.MainWindow.Format;
                string oriSrc = (string)GetColumnValueAtRow(rowId, Source, Global.MainWindow.PreviousPage);
                string oriDest = (string)GetColumnValueAtRow(rowId, Destination, Global.MainWindow.PreviousPage);
                if (!format.IsOneOf(oriSrc))
                {
                    if (oriDest != null && format.IsOneOf(oriDest) && File.Exists(oriDest))
                    {
                        SetColumnValue(rowId, Source, oriDest);
                    }
                    else
                    {
                        RemoveRow(rowId);
                        return false;
                    }
                }
            }

            return true;
        }

        private bool UpdateDestinationColumn(int rowId)
        {
            bool visible = IsColumnVisible(Destination);
            if (rowId == 0)
            {
                Columns[Destination.Index].Visible = visible;
            }

            if (rowId > 0 && Global.MainWindow.TabPageChanging && visible)
            {
                string sourceFileName = (string)GetColumnValueAtRow(rowId, Source);
                string destFileName = GetDestination(sourceFileName);

                if (ColumnExists(rowId, Destination))
                {
                    this[rowId, Destination.Index].Value = destFileName;
                }
                else
                {
                    CreateDestinationCell(rowId, destFileName);
                }
            }

            return true;
        }

        private bool UpdateFromToColumn(int rowId)
        {
            bool visible = IsColumnVisible(From);
            if (rowId == 0)
            {
                Columns[From.Index].Visible = visible;
                Columns[To.Index].Visible = visible;
                return true;
            }

            if (rowId > 0 && Global.MainWindow.TabPageChanging && visible)
            {
                CreateFromToCell(rowId, (string)GetColumnValueAtRow(rowId, Source));
            }

            return true;
        }

        public void ChangeCheckBox(ColumnHead head, bool? isChecked)
        {
            if (isChecked != null)
            {
                for (int i = 1; i < RowsCount; ++i)
                {
                    SetCheckBoxByColumn(i, head, isChecked);
                }
            }
        }

        private void UpdateCheckBoxByColumn(int rowId, ColumnHead head, bool? isChecked)
        {
            bool visible = IsColumnVisible(head);
            if (rowId == 0)
            {
                Columns[head.Index].Visible = visible;
                UpdatePdfLetsFlags();
                return;
            }

            SetCheckBoxByColumn(rowId, head, isChecked);
        }

        private void UpdateCheckBoxByColumn(int rowId, CheckBoxColumnHead head)
        {
            UpdateCheckBoxByColumn(rowId, head, Helper.CheckStateToNullableBool(head.CheckState));
        }

        private bool UpdateCheckBoxColumns(int rowId)
        {
            UpdateCheckBoxByColumn(rowId, Bookmark);
            UpdateCheckBoxByColumn(rowId, Hyperlink);
            UpdateCheckBoxByColumn(rowId, Navigator);

            return true;
        }

        private bool UpdateStatusColumn(int rowId)
        {
            if (rowId > 0 && Global.MainWindow.TabPageChanging)
            {
                this[rowId, Status.Index].Value = ItemStatus.Idle.ToString();
            }

            return true;
        }

        private bool UpdatePasswordColumn(int rowId)
        {
            bool visible = IsColumnVisible(Password);
            if (rowId == 0)
            {
                Columns[Password.Index].Visible = visible;
            }

            if (rowId > 0 && Global.MainWindow.TabPageChanging && visible)
            {
                if (!ColumnExists(rowId, Password))
                {
                    CreatePasswordCell(rowId);
                }
            }

            return true;
        }

        #endregion

        #region Helper methods

        public object GetColumnValueAtRow(int rowId, ColumnHead head)
        {
            return GetColumnValueAtRow(rowId, head, Global.CurrentPage);
        }

        public object GetColumnValueAtRow(int rowId, ColumnHead head, TataRibbonPage page)
        {
            if (rowId <= 0 || head == null || page == null)
            {
                return null;
            }

            if (!IsColumnVisible(head, page))
            {
                return null;
            }
            SourceGrid.Cells.Cell cell = (SourceGrid.Cells.Cell)this[rowId, head.Index];
            if (cell == null)
            {
                return null;
            }

            return cell.Value;
        }

        private void SetCheckBoxByColumn(int rowId, ColumnHead head, bool? isChecked)
        {
            if (isChecked == null)
            {
                isChecked = false;
            }

            if (!ColumnExists(rowId, head))
            {
                this[rowId, head.Index] = CreateCheckBox();
                SetCheckBoxByColumn(rowId, head, isChecked);
            }
            else
            {
                SetColumnValue(rowId, head, isChecked);
            }
        }

        private void SetColumnValue(int rowId, ColumnHead head, object value)
        {
            if ( ! ColumnExists(rowId, head))
            {
                this[rowId, head.Index] = CreateRegularCell(value);
            }
            else
            {
                this[rowId, head.Index].Value = value;
            }
        }

        private delegate void FromToCellsUpdator(TataGridItem item, int numOfPages);

        private void UpdateFromToCells(TataGridItem item, int numOfPages)
        {
            lock (this)
            {
                while (true)
                {
                    string itemFileName = (string)GetColumnValueAtRow(item.RowIndex, Source);
                    if (itemFileName == item.FileName)
                    {
                        //matched, then we update from/to cells
                        SourceGrid.Cells.Cell fromCell = (SourceGrid.Cells.Cell)this[item.RowIndex, From.Index];
                        SourceGrid.Cells.Cell toCell = (SourceGrid.Cells.Cell)this[item.RowIndex, To.Index];

                        if (numOfPages > 0)
                        {
                            //create editor
                            SourceGrid.Cells.Editors.NumericUpDown numericUpDownEditor = new
                                                SourceGrid.Cells.Editors.NumericUpDown(typeof(int), numOfPages, 1, 1);
                            numericUpDownEditor.EditableMode = EditableMode.SingleClick;
                            numericUpDownEditor.UseCellViewProperties = false;
                            numericUpDownEditor.Control.Font = editorFont;

                            //update cells
                            if ((int)fromCell.Value == 0)
                            {
                                fromCell.Value = 1;
                            }
                            fromCell.Editor = numericUpDownEditor;

                            if ((int)toCell.Value == 0)
                            {
                                toCell.Value = numOfPages;
                            }
                            toCell.Editor = numericUpDownEditor;
                        }
                        else
                        {
                            fromCell.Value = toCell.Value = 0;

                            string errorMsg = Resources.InvalidFileItemsTip;
                            errorMsg = errorMsg.Replace("{file}", Path.GetFileName(itemFileName));
                            Global.ShowNotification( errorMsg );
                            //select to highlight the row to show the user that it's invalid
                            Selection.SelectRow(item.RowIndex, true);
                        }

                        return;
                    }
                }
            }
        }

        private void CalculateNumberOfPagesAndUpdateCells(object sender, DoWorkEventArgs e)
        {
            TataGridItem item = (TataGridItem)e.Argument;

            int numOfPages = Global.CurrentPage.GetNumberOfPagesOfSourceFile(item.FileName);

            if (this.InvokeRequired)
            {
                object[] paras = new object[2] { item, numOfPages };
                Invoke(new FromToCellsUpdator(UpdateFromToCells), paras);
            }
            else
            {
                UpdateFromToCells(item, numOfPages);
            }
        }

        private void CreateFromToCell(int rowId, string item)
        {
            SourceGrid.Cells.Cell fromCell = CreateRegularCell(0);
            SourceGrid.Cells.Cell toCell = CreateRegularCell(0);
            this[rowId, From.Index] = fromCell;
            this[rowId, To.Index] = toCell;

            if (rowId > 1)
            {
                string prevItem = (string)GetColumnValueAtRow(rowId - 1, Source);
                if (prevItem == item)
                {
                    SourceGrid.Cells.Cell prevFromCell = (SourceGrid.Cells.Cell)this[rowId - 1, From.Index];
                    SourceGrid.Cells.Cell prevToCell = (SourceGrid.Cells.Cell)this[rowId - 1, To.Index];

                    if (prevFromCell != null && prevToCell != null)
                    {
                        SourceGrid.Cells.Editors.NumericUpDown editor =
                            (SourceGrid.Cells.Editors.NumericUpDown)prevFromCell.Editor;
                        if (editor != null)
                        {
                            fromCell.Editor = toCell.Editor = editor;
                            int numberOfPages = (int)editor.Control.Maximum;
                            int from = (int)prevFromCell.Value;
                            int to = (int)prevToCell.Value;
                            if (from > to)
                            {
                                int tmp = from;
                                from = to;
                                to = tmp;
                            }

                            if (to < numberOfPages)
                            {
                                fromCell.Value = to + 1;
                            }
                            else
                            {
                                fromCell.Value = 1;
                            }

                            toCell.Value = numberOfPages;

                            return;
                        }
                    }
                }
            }

            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += new DoWorkEventHandler(CalculateNumberOfPagesAndUpdateCells);

            worker.RunWorkerAsync(new TataGridItem(item, rowId));
        }

        private void CreatePasswordCell(int rowId)
        {
            PasswordString cellValue = PasswordString.Empty;

            SourceGrid.Cells.Cell passwordCell = new SourceGrid.Cells.Cell(cellValue, passwordEditor);

            this[rowId, Password.Index] = passwordCell;
        }

        private bool ColumnExists(int rowId, ColumnHead head)
        {
            return this[rowId, head.Index] != null;
        }

        private static string GetDestination(string item, string directory)
        {
            return Global.CurrentPage.GetDestination(item, directory);
        }

        private static string GetDestination(string item)
        {
            return GetDestination(item, string.Empty);
        }

        private static bool IsColumnVisible(ColumnHead head)
        {
            return IsColumnVisible(head, Global.CurrentPage);
        }

        private static bool IsColumnVisible(ColumnHead head, TataRibbonPage page)
        {
            return page.IsColumnVisible(head);
        }

        private void SetCreatorAndUpdator()
        {
            ID.Creator = CreatorIDColumn;
            ID.Updator = UpdateIDColumn;
            Source.Creator = CreateSourceColumn;
            Source.Updator = UpdateSourceColumn;
            Destination.Creator = CreateDestinationColumn;
            Destination.Updator = UpdateDestinationColumn;
            From.Creator = CreateFromToColumns;
            From.Updator = UpdateFromToColumn;

            Bookmark.Creator = CreateCheckBoxColumns;
            Bookmark.Updator = UpdateCheckBoxColumns;
            Status.Creator = CreateStatusColumn;
            Status.Updator = UpdateStatusColumn;

            Password.Creator = CreatePasswordColumn;
            Password.Updator = UpdatePasswordColumn;
        }

        private void CreateDestinationCell(int rowId, string item)
        {
            this[rowId, Destination.Index] = CreateFileNameCell(destinationCellTooltip, destinatinCellEditor, item); ;
        }
        #endregion

        #endregion
    }
}
